home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet bezpieczenstwa / mini Pentoo LiveCD 2006.1 / mpentoo-2006.1.iso / livecd.squashfs / usr / lib / portage / bin / dispatch-conf < prev    next >
Encoding:
Text File  |  2006-06-30  |  11.5 KB  |  320 lines

  1. #!/usr/bin/python -O
  2. # Copyright 1999-2006 Gentoo Foundation
  3. # Distributed under the terms of the GNU General Public License v2
  4. # $Id: /var/cvsroot/gentoo-src/portage/bin/dispatch-conf,v 1.7.2.10 2005/05/12 15:20:22 jstubbs Exp $
  5.  
  6. #
  7. # dispatch-conf -- Integrate modified configs, post-emerge
  8. #
  9. #  Jeremy Wohl (http://igmus.org)
  10. #
  11. # TODO
  12. #  dialog menus
  13. #
  14.  
  15. from stat import *
  16. from random import *
  17. import os, shutil, sys, string, re, commands, atexit
  18. sys.path = ["/usr/lib/portage/pym"]+sys.path
  19.  
  20. import portage, dispatch_conf
  21.  
  22. FIND_EXTANT_CONFIGS  = "find %s/ -iname '._cfg????_*' | sed -e 's://:/:g'"
  23. DIFF_CONTENTS        = 'diff -Nu %s %s'
  24. DIFF_CVS_INTERP      = 'diff -Nu %s %s | grep "^[+-][^+-]" | grep -v "# .Header:.*"'
  25. DIFF_WSCOMMENTS      = 'diff -Nu %s %s | grep "^[+-][^+-]" | grep -v "^[-+]#" | grep -v "^[-+][:space:]*$"'
  26.  
  27. # We need a secure scratch dir and python does silly verbose errors on the use of tempnam
  28. oldmask = os.umask(0077)
  29. SCRATCH_DIR = None
  30. while SCRATCH_DIR is None:
  31.     try:
  32.         mydir = "/tmp/dispatch-conf."
  33.         for x in range(0,8):
  34.             if int(random() * 3) == 0:
  35.                 mydir += chr(int(65+random()*26.0))
  36.             elif int(random() * 2) == 0:
  37.                 mydir += chr(int(97+random()*26.0))
  38.             else:
  39.                 mydir += chr(int(48+random()*10.0))
  40.         if os.path.exists(mydir):
  41.             continue
  42.         os.mkdir(mydir)
  43.         SCRATCH_DIR = mydir
  44.     except OSError, e:
  45.         if e.errno != 17:
  46.             raise
  47. os.umask(oldmask)
  48.  
  49. # Ensure the scratch dir is deleted
  50. def cleanup(mydir=SCRATCH_DIR):
  51.     shutil.rmtree(mydir)
  52. atexit.register(cleanup)
  53.  
  54. MANDATORY_OPTS  = [ 'archive-dir', 'diff', 'replace-cvs', 'replace-wscomments', 'merge' ]
  55.  
  56. class dispatch:
  57.     options = {}
  58.  
  59.     def grind (self, config_paths):
  60.         confs = []
  61.         count = 0
  62.  
  63.  
  64.         self.options = dispatch_conf.read_config(MANDATORY_OPTS)
  65.  
  66.         if self.options.has_key("log-file"):
  67.             if os.path.isfile(self.options["log-file"]):
  68.                 shutil.copy(self.options["log-file"], self.options["log-file"] + '.old')
  69.             if os.path.isfile(self.options["log-file"]) \
  70.                or not os.path.exists(self.options["log-file"]):
  71.                 open(self.options["log-file"], 'w').close() # Truncate it
  72.                 os.chmod(self.options["log-file"], 0600)
  73.         else:
  74.             self.options["log-file"] = "/dev/null"
  75.  
  76.         #
  77.         # Build list of extant configs
  78.         #
  79.  
  80.         for path in config_paths.split ():
  81.             if not os.path.exists (path):
  82.                 continue
  83.  
  84.             confs += self.massage (os.popen (FIND_EXTANT_CONFIGS % (path,)).readlines ())
  85.  
  86.         if self.options['use-rcs'] == 'yes' and ((os.system( "which rcs >/dev/null 2>&1" ) == 256)
  87.                                                  or (os.system( "which ci >/dev/null 2>&1" ) == 256)
  88.                                                  or (os.system( "which co >/dev/null 2>&1" ) == 256) 
  89.                                                  or (os.system( "which rcsmerge >/dev/null 2>&1" ) == 256)):
  90.             print >> sys.stderr, 'dispatch-conf: Error finding all RCS utils and use-rcs=yes in config; fatal'
  91.             return False
  92.  
  93.  
  94.         #
  95.         # Remove new configs identical to current
  96.         #                  and
  97.         # Auto-replace configs a) whose differences are simply CVS interpolations,
  98.         #                  or  b) whose differences are simply ws or comments,
  99.         #                  or  c) in paths now unprotected by CONFIG_PROTECT_MASK,
  100.         #
  101.  
  102.         def f (conf):
  103.             mrgconf = re.sub(r'\._cfg', '._mrg', conf['new'])
  104.             archive = os.path.join(self.options['archive-dir'], conf['current'].lstrip('/'))
  105.             if self.options['use-rcs'] == 'yes':
  106.                 mrgfail = dispatch_conf.rcs_archive(archive, conf['current'], conf['new'], mrgconf)
  107.             else:
  108.                 mrgfail = dispatch_conf.file_archive(archive, conf['current'], conf['new'], mrgconf)
  109.             if os.path.exists(archive + '.dist'):
  110.                 unmodified = len(commands.getoutput(DIFF_CONTENTS % (conf['current'], archive + '.dist'))) == 0
  111.             else:
  112.                 unmodified = 0
  113.             if os.path.exists(mrgconf):
  114.                 if mrgfail or len(commands.getoutput(DIFF_CONTENTS % (conf['new'], mrgconf))) == 0:
  115.                     os.unlink(mrgconf)
  116.                     newconf = conf['new']
  117.                 else:
  118.                     newconf = mrgconf
  119.             else:
  120.                 newconf = conf['new']
  121.  
  122.             same_file = len(commands.getoutput (DIFF_CONTENTS   % (conf ['current'], newconf))) == 0
  123.             same_cvs  = len(commands.getoutput (DIFF_CVS_INTERP % (conf ['current'], newconf))) == 0
  124.             same_wsc  = len(commands.getoutput (DIFF_WSCOMMENTS % (conf ['current'], newconf))) == 0
  125.  
  126.             # Do options permit?
  127.             same_cvs = same_cvs and self.options['replace-cvs'] == 'yes'
  128.             same_wsc = same_wsc and self.options['replace-wscomments'] == 'yes'
  129.             unmodified = unmodified and self.options['replace-unmodified'] == 'yes'
  130.  
  131.             if same_file:
  132.                 os.unlink (conf ['new'])
  133.                 self.post_process(conf['current'])
  134.                 if os.path.exists(mrgconf):
  135.                     os.unlink(mrgconf)
  136.                 return False
  137.             elif unmodified or same_cvs or same_wsc or conf ['dir'] in portage.settings ['CONFIG_PROTECT_MASK'].split ():
  138.                 self.replace(newconf, conf['current'])
  139.                 self.post_process(conf['current'])
  140.                 if newconf == mrgconf:
  141.                     os.unlink(conf['new'])
  142.                 elif os.path.exists(mrgconf):
  143.                     os.unlink(mrgconf)
  144.                 return False
  145.             else:
  146.                 return True
  147.  
  148.         confs = filter (f, confs)
  149.  
  150.         #
  151.         # Interactively process remaining
  152.         #
  153.  
  154.         for conf in confs:
  155.             count = count + 1
  156.  
  157.             newconf = conf['new']
  158.             mrgconf = re.sub(r'\._cfg', '._mrg', newconf)
  159.             if os.path.exists(mrgconf):
  160.                 newconf = mrgconf
  161.             show_new_diff = 0
  162.  
  163.             while 1:
  164.                 if show_new_diff:
  165.                     os.system((self.options['diff']) % (conf['new'], mrgconf))
  166.                     show_new_diff = 0
  167.                 else:
  168.                     os.system((self.options['diff']) % (conf['current'], newconf))
  169.  
  170.                 print
  171.                 print '>> (%i of %i) -- %s' % (count, len(confs), conf ['current'])
  172.                 print '>> q quit, h help, n next, e edit-new, z zap-new, u use-new\n   m merge, t toggle-merge, l look-merge: ',
  173.  
  174.                 c = getch ()
  175.  
  176.                 if c == 'q':
  177.                     sys.exit (0)
  178.                 if c == 'h':
  179.                     self.do_help ()
  180.                     continue
  181.                 elif c == 't':
  182.                     if newconf == mrgconf:
  183.                         newconf = conf['new']
  184.                     elif os.path.exists(mrgconf):
  185.                         newconf = mrgconf
  186.                     continue
  187.                 elif c == 'n':
  188.                     break
  189.                 elif c == 'm':
  190.                     merged = SCRATCH_DIR+"/"+os.path.basename(conf['current'])
  191.                     print
  192.                     ret = os.system (self.options['merge'] % (merged, conf ['current'], newconf))
  193.                     if ret:
  194.                         print "Failure running 'merge' command"
  195.                         continue
  196.                     shutil.copyfile(merged, mrgconf)
  197.                     os.remove(merged)
  198.                     mystat = os.lstat(conf['new'])
  199.                     os.chmod(mrgconf, mystat[ST_MODE])
  200.                     os.chown(mrgconf, mystat[ST_UID], mystat[ST_GID])
  201.                     newconf = mrgconf
  202.                     continue
  203.                 elif c == 'l':
  204.                     show_new_diff = 1
  205.                     continue
  206.                 elif c == 'e':
  207.                     if not os.environ.has_key('EDITOR'):
  208.                         os.environ['EDITOR']='nano'
  209.                     os.system(os.environ['EDITOR'] + ' ' + newconf)
  210.                     continue
  211.                 elif c == 'z':
  212.                     os.unlink(conf['new'])
  213.                     if os.path.exists(mrgconf):
  214.                         os.unlink(mrgconf)
  215.                     break
  216.                 elif c == 'u':
  217.                     self.replace(newconf, conf ['current'])
  218.                     self.post_process(conf['current'])
  219.                     if newconf == mrgconf:
  220.                         os.unlink(conf['new'])
  221.                     elif os.path.exists(mrgconf):
  222.                         os.unlink(mrgconf)
  223.                     break
  224.                 else:
  225.                     continue
  226.  
  227.  
  228.     def replace (self, newconf, curconf):
  229.         """Replace current config with the new/merged version.  Also logs
  230.         the diff of what changed into the configured log file."""
  231.         os.system((DIFF_CONTENTS % (curconf, newconf)) + '>>' + self.options["log-file"])
  232.         try:
  233.             shutil.copyfile(newconf, curconf)
  234.             os.remove(newconf)
  235.         except (IOError, os.error), why:
  236.             print >> sys.stderr, 'dispatch-conf: Error renaming %s to %s: %s; fatal' % \
  237.                   (newconf, curconf, str(why))
  238.  
  239.  
  240.     def post_process(self, curconf):
  241.         archive = os.path.join(self.options['archive-dir'], curconf.lstrip('/'))
  242.         if self.options['use-rcs'] == 'yes':
  243.             dispatch_conf.rcs_archive_post_process(archive)
  244.         else:
  245.             dispatch_conf.file_archive_post_process(archive)
  246.  
  247.  
  248.     def massage (self, newconfigs):
  249.         """Sort, rstrip, remove old versions, break into triad hash.
  250.  
  251.         Triad is dictionary of current (/etc/make.conf), new (/etc/._cfg0003_make.conf)
  252.         and dir (/etc).
  253.  
  254.         We keep ._cfg0002_conf over ._cfg0001_conf and ._cfg0000_conf.
  255.         """
  256.         h = {}
  257.  
  258.         newconfigs.sort ()
  259.  
  260.         for nconf in newconfigs:
  261.             nconf = nconf.rstrip ()
  262.             conf  = re.sub (r'\._cfg\d+_', '', nconf)
  263.             dir   = re.match (r'^(.+)/', nconf).group (1)
  264.  
  265.             if h.has_key (conf):
  266.                 mrgconf = re.sub(r'\._cfg', '._mrg', h[conf]['new'])
  267.                 if os.path.exists(mrgconf):
  268.                     os.unlink(mrgconf)
  269.                 os.unlink(h[conf]['new'])
  270.  
  271.             h [conf] = { 'current' : conf, 'dir' : dir, 'new' : nconf }
  272.  
  273.         configs = h.values ()
  274.         configs.sort (lambda a, b: cmp(a ['current'], b ['current']))
  275.  
  276.         return configs
  277.  
  278.  
  279.     def do_help (self):
  280.         print; print
  281.  
  282.         print '  u -- update current config with new config and continue'
  283.         print '  z -- zap (delete) new config and continue'
  284.         print '  n -- skip to next config, leave all intact'
  285.         print '  e -- edit new config'
  286.         print '  m -- interactively merge current and new configs'
  287.         print '  l -- look at diff between pre-merged and merged configs'
  288.         print '  t -- toggle new config between merged and pre-merged state'
  289.         print '  h -- this screen'
  290.         print '  q -- quit'
  291.  
  292.         print; print 'press any key to return to diff...',
  293.  
  294.         getch ()
  295.  
  296.  
  297. def getch ():
  298.     # from ASPN - Danny Yoo
  299.     #
  300.     import sys, tty, termios
  301.  
  302.     fd = sys.stdin.fileno()
  303.     old_settings = termios.tcgetattr(fd)
  304.     try:
  305.         tty.setraw(sys.stdin.fileno())
  306.         ch = sys.stdin.read(1)
  307.     finally:
  308.         termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
  309.     return ch
  310.  
  311.  
  312. # run
  313. d = dispatch ()
  314.  
  315. if len(sys.argv) > 1:
  316.     # for testing
  317.     d.grind (string.join (sys.argv [1:]))
  318. else:
  319.     d.grind (portage.settings ['CONFIG_PROTECT'])
  320.